using System;
using System.IO;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;

using Microsoft.DirectX;
using Microsoft.DirectX.Direct3D;
using Gosubb;

namespace Gosubb
{
    public class GraphGrid
    {
        // Pointer to the d3d
        Graph3D mD3d = null;

        // Real scales
        float[] mXscale = null;
        float[] mYscale = null;
        int mXinv = 1;
        int mYinv = 1;
        
        // Normalised Scales
        float[] mXscaleNorm = null;
        float[] mYscaleNorm = null;

        // Real data
        float[,] mData = null;
        Color32[,] mColour = null;
        bool mDrawText = false;

        // Normalised and adjusted data
        Vector3[,] mVectors = null;
        CustomVertex.PositionColored[] mGraphLines = null;
        CustomVertex.PositionColored[] mGraphMesh = null;

        GraphText[] mTextArray;

        float mMax=10;
        float mMin=0;

        public void NormaliseScale(ref float[] scale, ref float[] oScale, int inv)
        {
            if (scale != null)
            {
                float min, max, range;
                float[] processScale = new float[scale.Length];

                min = scale[0];
                max = scale[0];
                for (int x = 1; x < scale.Length; x++)
                {
                    if (scale[x] < min)
                    {
                        min = scale[x];
                    }
                    if (scale[x] > max)
                    {
                        max = scale[x];
                    }
                }
                range = max - min;
                for (int x = 0; x < scale.Length; x++)
                {
                    if (inv == -1)
                    {
                        processScale[x] = ((1-((scale[x] - min) / range)) -0.5F);
                    }
                    else
                    {
                        processScale[x] = ((scale[x] - min) / range) -0.5F;
                    }
                }
                oScale = processScale;
            }
        }

        public void MakeNormalise()
        {
            if (mXscaleNorm != null & mYscaleNorm != null & mData != null & mColour != null)
            {
                mVectors = new Vector3[mXscaleNorm.Length, mYscaleNorm.Length];

                if (mXscaleNorm.Length > 0 & mYscaleNorm.Length > 0 & mData.Length > 0)
                {
                    if (mData.GetUpperBound(0) == mXscaleNorm.GetUpperBound(0) &
                        mData.GetUpperBound(1) == mYscaleNorm.GetUpperBound(0))
                    {
                        for (int x = 0; x < mXscaleNorm.Length; x++)
                        {
                            for (int y = 0; y < mYscaleNorm.Length; y++)
                            {
                                mVectors[x, y] = new Vector3(mXscaleNorm[x] , mYscaleNorm[y] , ((mData[x, y] - mMin) / (mMax - mMin)));
                            }
                        }
                        MakeVerts();
                        MakeText();
                        if (mD3d != null) { mD3d.ReGenerate(); }
                    }
                }
            }
        }

        private void MakeText()
        {
            if (this.DrawText)
            {
                if (mYscaleNorm != null & mXscaleNorm != null)
                {
                    if (mYscaleNorm.Length > 0 & mXscaleNorm.Length > 0)
                    {
                        mTextArray = new GraphText[mXscaleNorm.Length + mYscaleNorm.Length];
                        int count = 0;
                        for (int x = 0; x < mXscaleNorm.Length; x++)
                        {
                            mTextArray[count] = new GraphText();
                            mTextArray[count].Text = mXscale[x].ToString();
                            mTextArray[count].Rotation = -90;
                            mTextArray[count].ScaleX = true;
                            mTextArray[count++].Position = new Vector3(mXscaleNorm[x], -0.6F,0);
                        }
                        for (int y = 0; y < mYscaleNorm.Length; y++)
                        {
                            mTextArray[count] = new GraphText();
                            mTextArray[count].Text = mYscale[y].ToString();
                            mTextArray[count].Rotation = 0;
                            mTextArray[count].ScaleX = false;
                            mTextArray[count++].Position = new Vector3(-0.6F, mYscaleNorm[y], 0);
                        }
                    }
                }
            }
        }

        private void MakeVerts()
        {
            int wX = mXscaleNorm.Length;
            int wY = mYscaleNorm.Length;

            if (wX > 1 & wY > 1)
            {
                int verts = wX * wY * 6;
                int lines = ((wX * (wY - 1)) + (wY * (wX - 1))) * 2;
                int count = 0;

                // check to see if the scales match the vectors now make the verticies
                if (wX - 1 == mVectors.GetUpperBound(0) & wY - 1 == mVectors.GetUpperBound(1))
                {
                    mGraphLines = new CustomVertex.PositionColored[lines];
                    mGraphMesh = new CustomVertex.PositionColored[verts];
                    for (int x = 0; x < wX - 1; x++)
                    {
                        for (int y = 0; y < wY - 1; y++)
                        {
                            mGraphMesh[count].Position = mVectors[x, y];
                            mGraphMesh[count++].Color = mColour[x, y];
                            mGraphMesh[count].Position = mVectors[x + 1, y];
                            mGraphMesh[count++].Color = mColour[x + 1, y];
                            mGraphMesh[count].Position = mVectors[x + 1, y + 1];
                            mGraphMesh[count++].Color = mColour[x + 1, y + 1];
                            mGraphMesh[count].Position = mVectors[x, y];
                            mGraphMesh[count++].Color = mColour[x, y];
                            mGraphMesh[count].Position = mVectors[x + 1, y + 1];
                            mGraphMesh[count++].Color = mColour[x + 1, y + 1];
                            mGraphMesh[count].Position = mVectors[x, y + 1];
                            mGraphMesh[count++].Color = mColour[x, y + 1];
                        }
                    }
                    count = 0;
                    for (int x = 0; x < wX; x++)
                    {
                        for (int y = 0; y < wY - 1; y++)
                        {
                            mGraphLines[count].Position = mVectors[x, y];
                            mGraphLines[count++].Color = new Color32(255, mColour[x, y].Brighten(-50));
                            mGraphLines[count].Position = mVectors[x, y + 1];
                            mGraphLines[count++].Color = new Color32(255, mColour[x, y].Brighten(-50));
                        }
                    }
                    for (int y = 0; y < wY; y++)
                    {
                        for (int x = 0; x < wX - 1; x++)
                        {
                            mGraphLines[count].Position = mVectors[x, y];
                            mGraphLines[count++].Color = new Color32(255, mColour[x, y].Brighten(-50));
                            mGraphLines[count].Position = mVectors[x + 1, y];
                            mGraphLines[count++].Color = new Color32(255, mColour[x, y].Brighten(-50));
                        }
                    }
                }
            }
            else if (wY == 1 | wX == 1)
            {
                int verts = (wY+wX-2) * 6;
                int lines = verts - 4;
                int count = 0;
                int lCount = 0;
                if (wY > 0 & wX > 0)
                {
                    mGraphLines = new CustomVertex.PositionColored[lines];
                    mGraphMesh = new CustomVertex.PositionColored[verts];
                    if (wX == 1)
                    {
                        int x = 0;
                        for (int y = 0; y < wY - 1; y++)
                        {
                            mGraphMesh[count].Position = new Vector3(mVectors[x, y].X, mVectors[x, y].Y, this.Min);
                            mGraphMesh[count++].Color = mColour[x, y];
                            mGraphLines[lCount++] = mGraphMesh[count - 1];
                            mGraphMesh[count].Position = mVectors[x, y];
                            mGraphMesh[count++].Color = mColour[x, y];
                            mGraphLines[lCount++] = mGraphMesh[count - 1];
                            mGraphLines[lCount++] = mGraphMesh[count - 1];
                            mGraphMesh[count].Position = mVectors[x, y+1];
                            mGraphMesh[count++].Color = mColour[x, y+1];
                            mGraphLines[lCount++] = mGraphMesh[count - 1];
                            mGraphMesh[count++] = mGraphMesh[count - 3];
                            mGraphMesh[count].Position = mVectors[x, y + 1];
                            mGraphMesh[count++].Color = mColour[x, y + 1];
                            mGraphMesh[count].Position = new Vector3(mVectors[x, y+1].X, mVectors[x, y+1].Y, this.Min);
                            mGraphMesh[count++].Color = mColour[x, y + 1];
                        }
                        mGraphLines[lCount++] = mGraphMesh[count - 2];
                        mGraphLines[lCount++] = mGraphMesh[count - 1];
                    }
                    else
                    {
                        int y = 0;
                        for (int x = 0; x < wX - 1; x++)
                        {
                            mGraphMesh[count].Position = new Vector3(mVectors[x, y].X, mVectors[x, y].Y, this.Min);
                            mGraphMesh[count++].Color = mColour[x, y];
                            mGraphLines[lCount++] = mGraphMesh[count - 1];
                            mGraphMesh[count].Position = mVectors[x, y];
                            mGraphMesh[count++].Color = mColour[x, y];
                            mGraphLines[lCount++] = mGraphMesh[count - 1];
                            mGraphLines[lCount++] = mGraphMesh[count - 1];
                            mGraphMesh[count].Position = mVectors[x+1, y];
                            mGraphMesh[count++].Color = mColour[x+1, y];
                            mGraphLines[lCount++] = mGraphMesh[count - 1];
                            mGraphMesh[count++] = mGraphMesh[count - 3];
                            mGraphMesh[count].Position = mVectors[x+1, y];
                            mGraphMesh[count++].Color = mColour[x+1, y ];
                            mGraphMesh[count].Position = new Vector3(mVectors[x+1, y].X, mVectors[x+1, y].Y, this.Min);
                            mGraphMesh[count++].Color = mColour[x+1, y];
                        }
                        mGraphLines[lCount++] = mGraphMesh[count - 2];
                        mGraphLines[lCount++] = mGraphMesh[count - 1];
                    }
                }
            }
        }

        public float Max
        {
            get { return mMax; }
            set { mMax = value; }
        }
        public float Min
        {
            get { return mMin; }
            set { mMin = value; }
        }
        public float[] scaleX
        {
            set
            {
                mXscale = value;
                NormaliseScale(ref mXscale, ref mXscaleNorm,mXinv);
                MakeText();
            }
            get
            {
                return mXscale;
            }
        }
        public float[] scaleY
        {
            set
            {
                mYscale = value;
                NormaliseScale(ref mYscale, ref mYscaleNorm,mYinv);
                MakeText();
            }
            get{return mYscale;}
        }
        public float[,] Data
        {
            set
            {
                mData = value;
            }
        }
        public Color32[,] Colour
        {
            set
            {
                mColour = value;
            }
        }
        public bool DrawText
        {
            get { return mDrawText; }
            set 
            { 
                mDrawText = value;
                MakeText();
            }
        }
        public GraphText[] TextArray
        {
            get { return mTextArray; }
        }

        // Mesh/Lines Export properties
        public CustomVertex.PositionColored[] GraphLines
        {
            get { return mGraphLines; }
        }
        public CustomVertex.PositionColored[] GraphMesh
        {
            get { return mGraphMesh; }
        }

        public int scaleXinvert
        {
            set
            { mXinv = value;}
            get
            { return mXinv;}
        }
        public int scaleYinvert
        {
            set
            { mYinv = value; }
            get
            { return mYinv; }
        }

        // set this every time when its put back into the graph3D class..

        public Graph3D D3d 
        { 
            get { return mD3d; } 
            set { mD3d = value;}
        }

        public void Dispose()
        {
            mD3d = null;
            mXscale = null;
            mYscale = null;

            // Normalised Scales
            mXscaleNorm = null;
            mYscaleNorm = null;

            // Real data
            mData = null;
            mColour = null;
            mDrawText = false;

            // Normalised and adjusted data
            mVectors = null;
            mGraphLines = null;
            mGraphMesh = null;

            mTextArray = null;
        }

    }

    public class GraphText
    {
        string mText="";
        Vector3 mPosition ;
        float mRotation=0;
        bool mScaleX = false;

        public bool ScaleX
        {
            set { mScaleX = value; }
            get { return mScaleX; }
        }
        public string Text
        {
            get { return mText; }
            set { mText = value; }
        }
        public Vector3 Position
        {
            get { return mPosition; }
            set { mPosition = value; }
        }
        public float Rotation
        {
            get { return mRotation; }
            set { mRotation = value * ((float)Math.PI / 180); ; }
        }
    }

    public partial class Graph3D : Gosubb.Direct3d
    {
        string mTableDesc = "";
        Color32 mBackgroundColor = Color.Black;

        GraphGrid mGridPrimary = null;
        GraphGrid mGridBase = null;
        float lookAngleUD = (float)Math.PI / 8;
        float lookAngleLH = (float)Math.PI / 2;
        float mMin = 0;
        float mMax = 10;

        AutoVertexBuffer mDataVertex;
        AutoVertexBuffer mDataLines;
        AutoVertexBuffer mBaseVertex;
        AutoVertexBuffer mBaseLines;
        AutoMesh[] mTextMeshes;
        Vector3[] mTextCenters;

        public Graph3D()
        {
            //InitialiseGrid();
            //GenerateGrid(this, ref mDataVertex, ref mGridPrimary);
            InitializeComponent();
        }

        private void InitialiseGrid()
        {
            GraphGrid Grid = new GraphGrid();
            GraphGrid BBase = new GraphGrid();
            Grid.scaleX = new float[] { -7,-6,-5,-4,-3,-2,-1,0, 1,2,3,4,5,6,7,8};
            Grid.scaleY = new float[] { -7, -6, -5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8 };
            BBase.scaleX = Grid.scaleX;
            BBase.scaleY = Grid.scaleY;
            float[,] tmp = new float[16, 16];
            float[,] tmp3 = new float[16, 16];
            Color32[,] tmp2 = new Color32[16, 16];
           
            for (int x = 0; x < 16; x++)
            {
                for (int y = 0; y < 16; y++)
                {
                    tmp[x, y] = (x+y)/4;
                    tmp2[x,y] = new Color32(255-x-y, x*16, y*16,255-x*16);
                    tmp3[x, y] = 0;
                }
            }
            Grid.Colour = tmp2;
            Grid.Data = tmp;
            BBase.Data = tmp3;
            BBase.Colour = tmp2;
            BBase.DrawText = true;
            BBase.Min = 10;
            BBase.Max = 20;
            this.setBaseGrid(ref BBase);
            this.setMainGrid(ref Grid);
        }

        public float Max
        {
            set { mMax = value; }
            get { return mMax; }
        }
        public float Min
        {
            set {mMin = value;}
            get { return mMin;}
}

        public GraphGrid getMainGrid()
        {
            return mGridPrimary;
        }
        public void setMainGrid(ref GraphGrid gIn)
        {
            if (mGridPrimary != null)
            { mGridPrimary.Dispose(); }
            mGridPrimary = gIn;
            mGridPrimary.D3d = this;
            mGridPrimary.MakeNormalise();
            Direct3d.DxRenderAllControls();
        }

        public GraphGrid getBaseGrid()
        {
            return mGridBase;
        }
        public void setBaseGrid(ref GraphGrid gIn)
        {
            if (mGridBase != null)
            { mGridBase.Dispose(); }
            mGridBase = gIn;
            mGridBase.D3d = this;
            mGridBase.MakeNormalise();
            Direct3d.DxRenderAllControls();
        }

        public float LookUpDown
        {
            get 
            { 
                return lookAngleUD * (180/(float)Math.PI);
            }
            set
            {
                if ( value < 0)
                {
                    lookAngleUD = 0F;
                }
                else if (value > 178)
                {
                    lookAngleUD = 176 * ((float)Math.PI) / 180;
                }
                else
                {
                    lookAngleUD = value * ((float)Math.PI) / 180;
                }
                Direct3d.DxRenderAllControls();
            }
        }
        public float LookLeftRight
        {
            get 
            {
                float val = (lookAngleLH * (float)((float)180 / (float)Math.PI));
                return val;
            }
            set
            {
                if (value < 0)
                {
                    lookAngleLH = (value+360) *((float)Math.PI /180);
                }
                else if (value > 360)
                {
                    lookAngleLH = (value-360) * ((float)Math.PI /180);
                }
                else
                {
                    lookAngleLH = (value * ((float)Math.PI) / 180);
                }
                Direct3d.DxRenderAllControls();
            }
        }
        [DefaultValue("")]
        public string BoundTable
        {
            get { return mTableDesc; }
            set { mTableDesc = value; }
        }

        protected override void OnPaint(PaintEventArgs e)
        {
            base.OnPaint(e);

            if (this.Dx != null)
                Direct3d.DxRenderAllControls();
        }

        public override void Refresh()
        {
            base.Refresh();
            if (this.Dx != null)
                Direct3d.DxRenderAllControls();
        }

        public void GenerateAlphaMap(Texture texture, int minAlpha, int maxAlpha, int minColor, int maxColor)
        {
            // Generate an alphamap
            int width = texture.GetLevelDescription(0).Width;
            int height = texture.GetLevelDescription(0).Height;
            int pitch;
            Color32[] bm = (Color32[])texture.LockRectangle(typeof(Color32),
                                                            0, 0, out pitch, width * height);

            // Multiply by 3 because alpha calculation is R+G+B
            minColor *= 3;
            maxColor *= 3;

            for (int i = 0; i < bm.Length; i++)
            {
                Color32 color = bm[i];

                int colorSum = color.iR + color.iG + color.iB; // alpha = color*3

                // Calculate alpha
                int alpha;
                if (colorSum >= maxColor)
                    alpha = maxAlpha;
                else if (colorSum <= minColor)
                    alpha = minAlpha;
                else
                {
                    // Convert colorSum from (minColor..maxColor) to (minApha..maxAlpha)
                    alpha = (colorSum - minColor) * (maxAlpha - minAlpha) / (maxColor - minColor) + minAlpha;
                }

                bm[i] = new Color32(alpha, color);
            }
            texture.UnlockRectangle(0);
        }

        private Vector3 CalcNormal(Vector3 point1, Vector3 point2, Vector3 point3)
        {
            Vector3 retVector = Vector3.Cross(point2 - point1, point3 - point1);
            retVector = Vector3.Normalize(retVector);
            return retVector;
        }

        public void ReGenerate()
        {
            if (Dx != null)
            {
                Graph3D_DxLoaded(this, this.Dx);
            }
        }

        public void Clean()
        {
            if (mTextMeshes != null)
            {
                for (int i = 0; i < mTextMeshes.Length; i++)
                {
                    mTextMeshes[i].Dispose();
                }
            }
            if (mDataVertex != null)
            { mDataVertex.Dispose(); }
            if (mDataLines != null)
            {mDataLines.Dispose();}
            if (mBaseLines != null)
            { mBaseLines.Dispose();}
            if (mBaseVertex != null)
            { mBaseVertex.Dispose(); }
            if (mTextCenters != null)
            {mTextCenters = null;}
        }

        /// <summary>
        /// Occurs once after DirectX has been initialized for the first time.  
        /// Setup AutoMesh's, AutoVertexBuffer's, and AutoTexture's here.
        /// </summary>
        private void Graph3D_DxLoaded(Gosubb.Direct3d d3d, Microsoft.DirectX.Direct3D.Device dx)
        {
            if (dx != null)
            {

                Clean();

                //InitialiseGrid();
                GraphGrid Graph = mGridPrimary;
                GraphGrid Base = mGridBase;
                float dump;

                if (Graph != null)
                {
                    if (Graph.GraphLines != null)
                    {
                        if (Graph.GraphLines.Length > 0)
                        {
                            mDataLines = new AutoVertexBuffer(d3d, typeof(CustomVertex.PositionColored),
                                                                    Graph.GraphLines.Length, Usage.Dynamic,
                                                                    CustomVertex.PositionColored.Format,
                                                                    Pool.Default);
                            mDataLines.VB.SetData(Graph.GraphLines, 0, LockFlags.None);
                        }
                    }
                    if (Graph.GraphMesh != null)
                    {
                        if (Graph.GraphMesh.Length > 0)
                        {
                            mDataVertex = new AutoVertexBuffer(d3d, typeof(CustomVertex.PositionColored),
                                                                Graph.GraphMesh.Length, Usage.Dynamic,
                                                                CustomVertex.PositionColored.Format,
                                                                Pool.Default);
                            mDataVertex.VB.SetData(Graph.GraphMesh, 0, LockFlags.None);
                        }
                    }
                    if (Graph.DrawText)
                    {
                        if (Graph.TextArray.Length > 0)
                        {
                            mTextMeshes = new AutoMesh[Graph.TextArray.Length];
                            mTextCenters = new Vector3[Graph.TextArray.Length];
                            for (int i = 0; i < mTextMeshes.Length; i++)
                            {
                                mTextMeshes[i] = new AutoMesh(
                                    d3d,
                                    Mesh.TextFromFont(dx,
                                    new System.Drawing.Font("Arial", 8, FontStyle.Regular),
                                    Graph.TextArray[i].Text,
                                    0.1F,
                                    15F));
                                //mTextMeshes[i].BoundingBox(out mTextCenters[i], out outDont);
                                dump = mTextMeshes[i].BoundingSphereMin(out mTextCenters[i]);
                            }
                        }
                    }
                }
                if (Base != null)
                {
                    if (Base.GraphLines != null)
                    {
                        if (Base.GraphLines.Length > 0)
                        {
                            mBaseLines = new AutoVertexBuffer(d3d, typeof(CustomVertex.PositionColored),
                                                                 Base.GraphLines.Length, Usage.Dynamic,
                                                                 CustomVertex.PositionColored.Format,
                                                                 Pool.Default);
                            mBaseLines.VB.SetData(Base.GraphLines, 0, LockFlags.None);
                        }
                    }
                    if (Base.GraphMesh != null)
                    {
                        if (Base.GraphMesh.Length > 0)
                        {
                            mBaseVertex = new AutoVertexBuffer(d3d, typeof(CustomVertex.PositionColored),
                                                                Base.GraphMesh.Length, Usage.Dynamic,
                                                                CustomVertex.PositionColored.Format,
                                                                Pool.Default);
                            mBaseVertex.VB.SetData(Base.GraphMesh, 0, LockFlags.None);
                        }
                    }
                    if (Base.DrawText)
                    {
                        if (Base.TextArray.Length > 0)
                        {
                            mTextMeshes = new AutoMesh[Base.TextArray.Length];
                            mTextCenters = new Vector3[Base.TextArray.Length];
                            for (int i = 0; i < mTextMeshes.Length; i++)
                            {
                                mTextMeshes[i] = new AutoMesh(
                                    d3d,
                                    Mesh.TextFromFont(dx,
                                    new System.Drawing.Font("Arial", 1, FontStyle.Regular),
                                    Base.TextArray[i].Text,
                                    0.001F,
                                    0.05F));
                                //mTextMeshes[i].BoundingBox(out mTextCenters[i], out outDont);
                                dump = mTextMeshes[i].BoundingSphereMin(out mTextCenters[i]);
                            }
                        }
                    }
                }
            }
        }

        /// <summary>
        /// Occurs when it is time to render 3d objects.  Place all 3d
        /// drawing code in this event.
        /// </summary>
        private void Graph3D_DxRender3d(Gosubb.Direct3d d3d, Microsoft.DirectX.Direct3D.Device dx)
        {
            // Setup the lights
            if (dx != null)
            {
                dx.Lights[0].Enabled = true;
                dx.Lights[0].Type = LightType.Directional;
                dx.Lights[0].Direction = new Vector3(0, 0, -1);
                dx.Lights[0].Diffuse = Color.White;
                dx.Lights[0].Position = new Vector3(0, 0, -400);


                //dx.Lights[0].Enabled = true;
                //dx.Lights[0].Ambient = Color.White;
                dx.RenderState.Lighting = false;
                dx.RenderState.Ambient = Color.Red;
                dx.RenderState.CullMode = Cull.None;
                //dx.RenderState.AmbientColor = 40;

                // lookAngleLH = pitch;
                //lookAngleUD = 12;
                Vector3 EyeFrom = new Vector3(0, -2, 200);
                EyeFrom.TransformCoordinate(//Matrix.RotationAxis(EyeFrom,lookAngleUD * (((float)Math.PI)/180))
                    Matrix.RotationYawPitchRoll(0, lookAngleUD, 0)
                                            * Matrix.RotationZ(lookAngleLH));
                //Matrix.RotationAxis(new Vector3(0, 1, 0), lookAngleUD*(180/(float)Math.PI))
                //* Matrix.RotationAxis(new Vector3(0, 0, 1), lookAngleLH * (180 / (float)Math.PI)));
                //*Matrix.RotationAxis(new Vector3(0, 1.0F, 0), lookAngleUD)
                //*Matrix.RotationAxis(new Vector3(0, 0, 1.0F), lookAngleLH));
                // Set viewer		
                dx.Transform.View = Matrix.LookAtLH(EyeFrom,
                    new Vector3(0.0f, 0.0f, .50F), new Vector3(0.0f, 0.0f, 1.0f));
                //((mMax - mMin) / 2) + mMin)
                // Set projection matrix
                //if ( Bounds.Height > Bounds.Width )
                //dx.Transform.Projection = Matrix.OrthoLH(1.62F, 1.62F , 0, 2000);

                float scale = Math.Max((float)((Bounds.Height * 0.6F) / Bounds.Width), (float)(Bounds.Width / (Bounds.Height * 0.9F)));

                dx.Transform.Projection = Matrix.OrthoLH(1.62F * scale, 1.62F * ((float)Bounds.Height / (float)Bounds.Width) * scale, 0, 2000);
                //else
                //    dx.Transform.Projection = Matrix.OrthoLH(1.62F * ((float)Bounds.Height / (float)Bounds.Width), 1.62F, 0, 2000);
                //dx.Transform.Projection = Matrix.PerspectiveFovLH(
                //    (float)Math.PI / 4, 640f / 480f, 0, 2000.0f);
                dx.RenderState.NormalizeNormals = true;

                // int ticks = Environment.TickCount; // Safe, less accurate


                // --------------------------------------------------------------
                // Draw gosub logo texture
                // --------------------------------------------------------------
                // Setup to render texture
                //dx.SetTexture(0, mGosubTexture.T);
                // dx.Material = GraphicsUtility.InitMaterial(Color.White);
                dx.RenderState.AlphaBlendEnable = true;
                dx.RenderState.SourceBlend = Blend.SourceAlpha;
                dx.RenderState.DestinationBlend = Blend.InvSourceAlpha;
                dx.RenderState.ColorWriteEnable = ColorWriteEnable.RedGreenBlueAlpha;
                dx.RenderState.AlphaBlendOperation = BlendOperation.Add;
                dx.RenderState.ColorVertex = true;
                dx.RenderState.TwoSidedStencilMode = true;
                //dx.TextureState[0].AlphaOperation = TextureOperation.Modulate;
                dx.RenderState.PointSize = 2.0F;
                
                // Setup the world matrix to position (and scale and rotate) the model in 3d world space
                dx.Transform.World = Matrix.Translation(0, 0, 0)
                                     * Matrix.Scaling(1, 1, 1); // Make it bigger
                if (mBaseVertex != null)
                {
                    dx.VertexFormat = mBaseVertex.VB.Description.VertexFormat;
                    dx.SetStreamSource(0, mBaseVertex.VB, 0);
                    dx.DrawPrimitives(PrimitiveType.TriangleList, 0, mBaseVertex.NumVertices / 3);
                }

                if (mBaseLines != null)
                {
                    dx.VertexFormat = mBaseLines.VB.Description.VertexFormat;
                    dx.SetStreamSource(0, mBaseLines.VB, 0);
                    dx.DrawPrimitives(PrimitiveType.LineList, 0, mBaseLines.NumVertices / 2);
                }

                //dx.VertexFormat = CustomVertex.PositionNormalColored.Format;
                if (mDataVertex != null)
                {
                    dx.VertexFormat = mDataVertex.VB.Description.VertexFormat;
                    dx.SetStreamSource(0, mDataVertex.VB, 0);
                    dx.DrawPrimitives(PrimitiveType.TriangleList, 0, mDataVertex.NumVertices / 3);
                }

                if (mDataLines != null)
                {
                    dx.VertexFormat = mDataLines.VB.Description.VertexFormat;
                    dx.SetStreamSource(0, mDataLines.VB, 0);
                    dx.DrawPrimitives(PrimitiveType.LineList, 0, mDataLines.NumVertices / 2);
                }
                dx.RenderState.Lighting = true;
                if (mTextMeshes != null & mTextCenters != null)
                {
                    if (mTextMeshes.Length > 0)
                    {
                        // Determine which grid is the text grid
                        GraphGrid ptGrid = null;
                        if (getMainGrid() != null && getMainGrid().DrawText)
                        { ptGrid = getMainGrid(); }
                        if (getBaseGrid() != null && getBaseGrid().DrawText)
                        { ptGrid = getBaseGrid(); }
                        if (ptGrid != null)
                        {
                            GraphText[] text = ptGrid.TextArray;
                            if (text != null)
                            {
                                Vector3 Position;
                                float modRotate = 0;
                                float modSideY = 1;
                                float modSideX = 1;
                                for (int i = 0; i < mTextMeshes.Length; i++)
                                {
                                    if (lookAngleLH < (float)Math.PI / 2)
                                    {
                                        if (!text[i].ScaleX)
                                        {
                                            modSideY = 1;
                                            modSideX = -1;
                                            modRotate = 0;
                                        }
                                        else
                                        {
                                            modRotate = (float)Math.PI;
                                            modSideY = 1;
                                            modSideX = 1;
                                        }
                                    }
                                    else if (lookAngleLH < (float)Math.PI)
                                    {
                                        if (!text[i].ScaleX)
                                        {
                                            modSideY = 1;
                                            modSideX = -1;
                                            modRotate = (float)Math.PI;
                                        }
                                        else
                                        {
                                            modRotate = (float)Math.PI;
                                            modSideX = 1;
                                            modSideY = -1;
                                        }
                                    }
                                    else if (lookAngleLH < (float)Math.PI * 1.5)
                                    {
                                        if (!text[i].ScaleX)
                                        {
                                            modSideX = 1;
                                            modSideY = 1;
                                            modRotate = (float)Math.PI;
                                        }
                                        else
                                        {
                                            modRotate = 0;//(float)Math.PI;
                                            modSideY = -1;
                                            modSideX = 1;
                                        }
                                    }
                                    else
                                    {
                                        if (!text[i].ScaleX)
                                        {
                                            modSideX = 1;
                                            modSideY = 1;
                                            modRotate = 0;
                                        }
                                        else
                                        {
                                            modSideX = 1;
                                            modSideY = 1;
                                            modRotate = 0;
                                        }
                                    }
                                    Position = new Vector3(text[i].Position.X * modSideX, text[i].Position.Y * modSideY, 0);

                                    dx.Transform.World = Matrix.Translation(-1 * mTextCenters[i])
                                                       * Matrix.Scaling(-0.05F , 0.05F , 0.05F )
                                                       * Matrix.RotationZ(text[i].Rotation + modRotate)
                                                       * Matrix.Translation(Position);

                                   //dx.Transform.World1 = Matrix.Scaling(0.5F, 0.5F, 0.5F);

                                    dx.Material = GraphicsUtility.InitMaterial(Color.Blue);
                                    dx.RenderState.Ambient = Color.Blue;
                                    mTextMeshes[i].M.DrawSubset(0);
                                }
                            }
                        }
                    }
                }
                //dx.RenderState.Lighting = true;
                dx.RenderState.AlphaBlendEnable = false;
                // dx.SetTexture(0, null);
            }
        }


        void Graph3D_DxRender2d(Direct3d d3d, Microsoft.DirectX.Direct3D.Device dx, Microsoft.DirectX.Direct3D.Surface surface, System.Drawing.Graphics graphics)
        {
            graphics.DrawString(Direct3d.DxFPS.ToString(), this.Font, new SolidBrush(Color.Black), new PointF(20, 20));
        }

    }
}

